#include "pesagemcontroller.h"
#include "db/db.h"
#include "dao/pesagemdao.h"
#include <QDateTime>
#include "dispositivos/balancasimulada.h"
#include "dispositivos/filizolacs.h"
#include "dispositivos/filizolapluris.h"
#include "dispositivos/filizolaidm.h"
#include "dispositivos/filizolamfc.h"
#include "utils/log.h"

PesagemController::PesagemErrors PesagemController::pesagemErrors[3] = {
    {"Não existe produto na balança, repita\n a operação após colocar o produto na balança.", 2},
    {"Repita a operação informando o recipiente correto.", 2},
    {"O peso está instável, repita a operação.", 1}
};

PesagemController::PesagemController(QObject *parent) :
    QObject(parent)
{
    LOGD("BEGIN");
    pesagemDAO = new PesagemDAO();
    balanca = 0;
    worker = 0;
    sema = new QSemaphore(1);
    LOGD("END");
}

PesagemController::~PesagemController()
{
    LOGD("BEGIN");
    delete pesagemDAO;
    parar_leitura_balanca();
    LOGD("END");
}

Conf* PesagemController::conf()
{
    LOGD("BEGIN");
    LOGD("END");
    return this->m_conf;
}

void PesagemController::setConf(Conf* conf)
{
    LOGD("BEGIN");
    this->m_conf = conf;
    LOGD("END");
}

Balanca* PesagemController::__get_balanca()
{
    LOGD("BEGIN");
    Balanca *balanca;
    QString modelo_conf = m_conf->get_model_balanca();
    QString baud_rate = m_conf->get_baudrate_balanca();
    QString porta = m_conf->get_port_balanca();
    unsigned long timeout = 1000;

    if(modelo_conf == MODELO_BALANCA_FILIZOLA_CS) {
        balanca = new FilizolaCS(0);
    } else if(modelo_conf == MODELO_BALANCA_FILIZOLA_IDM) {
        balanca = new FilizolaIDM(0);
    } else if(modelo_conf == MODELO_BALANCA_FILIZOLA_PLURIS) {
        balanca = new FilizolaPluris(0);
    } else if(modelo_conf == MODELO_BALANCA_FILIZOLA_MFC) {
        balanca = new FilizolaMFC(0);
    } else {
        balanca = new BalancaSimulada(0);
    }

    balanca->setBaudrate(baud_rate);
    balanca->setPort(porta);
    balanca->setTimeoutInMillis(timeout);

    LOGD("END");
    return balanca;
}

PesagemController::ValidacaoPesoResult PesagemController::validar_peso(double peso_total, double tara)
{
    LOGD("BEGIN");
    ValidacaoPesoResult result = PESO_INSTAVEL;
    bool checado;
    Balanca *balanca;

    if(peso_total==0) {
        result = NAO_EXISTE_PRODUTO;
        goto END;
    }

    if(peso_total <= tara) {
        result = RECIPIENTE_INFORMADO_INCORRETAMENTE;
        goto END;
    }

    balanca = __get_balanca();
    sema->acquire(1);
    checado = balanca->checar_peso(peso_total);
    sema->release(1);
    delete balanca;

    if(checado) {
        result = PESO_VALIDO;
        goto END;
    }

END:
    LOGD("END");
    return result;
}

void PesagemController::salvarPesagem(double peso_total, qulonglong id_depoperacao, qulonglong id_depprod,
                                       qulonglong codprod, QString produto, qulonglong id_deppanela,
                                       qulonglong codpanela, QString panela, double tara)
{
    LOGD("BEGIN");
    QDateTime data = QDateTime::currentDateTime();
    Pesagem *pesagem;

    int validacaoResult = validar_peso(peso_total, tara);

    QString infotara;
    infotara.sprintf("%.3f",tara);

    QString infopesototal;
    infopesototal.sprintf("%.3f",peso_total);

    switch(validacaoResult) {
    case PESO_VALIDO:
        pesagem = new Pesagem();
        pesagem->setInfopesototal(infopesototal);
        pesagem->setInfopesagem(data.toString("yyyy-MM-dd hh:mm:ss"));
        pesagem->setInfopesagemhora_int(data.time().hour());
        pesagem->setId_depoperacao(id_depoperacao);
        pesagem->setId_depprod(id_depprod);
        pesagem->setCodprod(codprod);
        pesagem->setProdutodesc(produto);
        pesagem->setId_deppanela(id_deppanela);
        pesagem->setCodpanela(codpanela);
        pesagem->setPaneladesc(panela);
        pesagem->setInfotara(infotara);
        pesagem->setExcluido(0);

        if(!pesagemDAO->save(pesagem)) {
            LOGE("erro ao salvar!!");
            emit saveError(_MSG_ERRO_COMUNICACAO_BD,0);
        } else {
            emit saveSuccess();
        }

        delete pesagem;
        break;
    default:
        emit saveError(pesagemErrors[validacaoResult].mensagem,pesagemErrors[validacaoResult].severidade);
        break;
    }
    LOGD("END");
}

void PesagemController::salvar_pesagem(double peso_total, qulonglong id_depoperacao, qulonglong id_depprod,
                                       qulonglong codprod, QString produto, qulonglong id_deppanela,
                                       qulonglong codpanela, QString panela, double tara)
{
    LOGD("BEGIN");
    salvarPesagem(peso_total, id_depoperacao, id_depprod,
                  codprod, produto, id_deppanela,
                  codpanela, panela, tara);
/*
    PesagemWorker *worker = new PesagemWorker(0,this);

    QHash<QString, QVariant> args;
    args.insert("peso_total",QVariant(peso_total));
    args.insert("id_depoperacao",QVariant(id_depoperacao));
    args.insert("id_depprod",QVariant(id_depprod));
    args.insert("codprod",QVariant(codprod));
    args.insert("produto",QVariant(produto));
    args.insert("id_deppanela",QVariant(id_deppanela));
    args.insert("codpanela",QVariant(codpanela));
    args.insert("panela",QVariant(panela));
    args.insert("tara",QVariant(tara));

    worker->runInBackground(SLOT(salvarPesagem()),args);
*/
    LOGD("END");
}

bool PesagemController::lerBalanca()
{
    LOGD("BEGIN");
    QVariantMap dados;
    unsigned int intervalInMicroSeconds;

    sema->acquire(1);
    if(!lendoBalanca) {
        sema->release(1);
        goto ERR;
    }

    if(balanca) {
        delete balanca;
        balanca = 0;
    }

    balanca = __get_balanca();

    dados = balanca->get_dados();

    //intervalo entre uma leituras. Faz pequenos sleeps, pra ficar sempre verificando se
    //a leitura foi finalizada.
    intervalInMicroSeconds = balanca->readIntervalInSeconds()*1000000;

    sema->release(1);

    while(intervalInMicroSeconds>1000) {
        if(!lendoBalanca) goto ERR;
        usleep(1000);
        intervalInMicroSeconds -= 1000;
    }
    if(!lendoBalanca) goto ERR;
    usleep(intervalInMicroSeconds);

    emit novaLeitura(dados);

    LOGD("END");
    return true;

ERR:
    LOGD("END");
    return false;
}

void PesagemController::iniciar_leitura_balanca()
{
    LOGD("BEGIN");
    lendoBalanca = true;

    worker = new PesagemWorker(0,this);
    worker->runInBackground(SLOT(lerBalanca()));
    LOGD("END");
}

void PesagemController::parar_leitura_balanca()
{
    LOGD("BEGIN");
    sema->acquire(1);
    lendoBalanca = false;
    if(balanca) {
        balanca->fechar_conexao();
        delete balanca;
        balanca = 0;
    }
    sema->release(1);
    LOGD("END");
}


